package com.itshare.video.common.util;

import cn.hutool.http.HtmlUtil;
import com.itshare.video.common.aop.AopPropertyUtils;
import com.itshare.video.common.aop.xss.annontation.CleanXSS;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;

/**
 * 防止 xss注入攻击
 * @Author lyr
 * @create 2020/11/3 22:19
 */
public class XssUtil {
    /**
     * 防止 xss注入
     * @param content
     * @return
     */
    public static String cleanScript(String content) {
        if (content==null) {
            return content;
        }
        // System.out.println("调用了方法");
        return cleanXSS2(content);
        // return cleanXSS2(content);
    }
    public static String unescapeHtml(String content) {
        return HtmlUtil.unescape(content);
    }

    static List<Pattern> patterns = Arrays.asList(
            // Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'",
            //         Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            // Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'",
            //         Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile("</script>", Pattern.CASE_INSENSITIVE),
            Pattern.compile("<script(.*?)>",
                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile("eval\\((.*?)\\)",
                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile("e­xpression\\((.*?)\\)",
                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE),
            Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE),
            Pattern.compile("onload(.*?)=",
                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL)


    );


    private static String  cleanXSS2(String value) {
        if (value != null) {
            //推荐使用ESAPI库来避免脚本攻击,value = ESAPI.encoder().canonicalize(value);
            // 避免空字符串
            // value = value.replaceAll(" ", "");
            for (Pattern scriptPattern: patterns) {
                // 避免script 标签
                // Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);
                value = scriptPattern.matcher(value).replaceAll("");
            }
            // // 避免script 标签
            // Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);
            // value = scriptPattern.matcher(value).replaceAll("");
            // // 避免src形式的表达式
            // scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'",
            //         Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            // value = scriptPattern.matcher(value).replaceAll("");



            // scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"",
            //         Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            // value = scriptPattern.matcher(value).replaceAll("");
            // 删除单个的 </script> 标签
            // scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
            // value = scriptPattern.matcher(value).replaceAll("");
            // 删除单个的<script ...> 标签
            // scriptPattern = Pattern.compile("<script(.*?)>",
            //         Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            // value = scriptPattern.matcher(value).replaceAll("");
            // 避免 eval(...) 形式表达式
            // scriptPattern = Pattern.compile("eval\\((.*?)\\)",
            //         Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            // value = scriptPattern.matcher(value).replaceAll("");
            // 避免 e­xpression(...) 表达式
            // scriptPattern = Pattern.compile("e­xpression\\((.*?)\\)",
            //         Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            // value = scriptPattern.matcher(value).replaceAll("");
            // // 避免 javascript: 表达式
            // scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
            // value = scriptPattern.matcher(value).replaceAll("");
            // 避免 vbscript:表达式
            // scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
            // value = scriptPattern.matcher(value).replaceAll("");
            // 避免 onload= 表达式
            // scriptPattern = Pattern.compile("onload(.*?)=",
            //         Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
            // value = scriptPattern.matcher(value).replaceAll("");
        }
        return value;
    }





    /**
     * 将 HTML转义
     * @param html
     * @return
     */
    public static String escapeHtml(String html) {
        if (html==null) {
            return html;
        }
        return HtmlUtil.escape(html);
    }

    public static String escapeJSAndHtml(CleanXSS strategy,String htmlBody) {
        if (strategy.type()== CleanXSS.type.EscapeALL) {
            return XssUtil.escapeHtml(htmlBody);
        }else{
            //先移除 js, 再 进行 文本 处理，前端富文本需求
            String noJsText = XssUtil.cleanScript(htmlBody);
            return XssUtil.escapeHtml(noJsText);
        }
    }

    //递归移除 算法实现
    public static void removeXSSWithBean(Object value) throws IllegalAccessException {
        if (value == null) {
            return;
        }
        Class clazz = value.getClass();
        for (Field field : clazz.getDeclaredFields()) {
            if (!field.isAccessible()) {
                field.setAccessible(true);
            }
            Class fieldClazz = field.getType();
            //如果是 String 类型
            if (String.class.isAssignableFrom(fieldClazz) && fieldClazz.isAnnotationPresent(CleanXSS.class)) {
                //是 string
                String fieldValue = (String) field.get(value);
                if (fieldValue != null) {
                    CleanXSS cleanXSS2 = (CleanXSS) clazz.getAnnotation(CleanXSS.class);
                    String txtValue = XssUtil.escapeJSAndHtml(cleanXSS2,(String)fieldValue);
                    // if(cleanXSS2.type()== CleanXSS.type.EscapeALL) {
                    //     txtValue = XssUtil.escapeHtml((String) field.get(fieldValue));
                    // }else{
                    //     //先去js ,再转义
                    //     txtValue = XssUtil.cleanXSS((String)field.get(fieldValue));
                    //     // txtValue = XssUtil.escapeHtml(txtValue);
                    // }
                    //防止xss
                    field.set(value,  txtValue);
                }
            } else if (!(AopPropertyUtils.isJavaApi(fieldClazz))) {
                //如果不是 java的 api ,那就是自定义的 api
                Object fieldValue = field.get(value);
                if (fieldValue != null) {
                    //递归调用，移除 String 的 xss
                    removeXSSWithBean(fieldValue);

                }

                // });
            }
        }
    }

}

